package middleware

import (
	"bytes"
	"encoding/json"
	"gitee.com/liyunyi/log-app/internal/model"
	"gitee.com/liyunyi/log-app/internal/service"
	"gitee.com/liyunyi/log-app/pkg/util"
	"github.com/gin-gonic/gin"
	"reflect"
	"runtime"
	"strings"
)

/*
日志记录表所有字段：

	// 1.1：id 自增
	// 1.2：title 【请求处理方法中设置】
	// 1.3：business_type 【请求处理方法中设置】
	// 1.4：origin_value 【编辑方法中设置】
	// 1.5：value 【编辑方法中设置】
	// 1.6：request_method 获取请求方式
	// 1.7：url 获取请求URL
	// 1.8：method 获取请求方法名称【具体方法中获取 使用反射】
	// 1.9：param 获取请求参数
	// 1.10：result 获取返回数据
	// 1.11：status 获取请求处理状态
	// 1.12：error_msg：获取异常信息
	// 1.13：oper_id：获取操作人id
	// 1.14：oper_name：获取操作人名称
	// 1.15：dept_name：获取操作人部门名称
	// 1.16：ip：获取操作人客户端ip
	// 1.17：location：获取操作人操作地点
	// 1.18：oper_type：获取操作类型
	// 1.19：description：描述信息 【请求处理方法中设置】
	// 1.20：created_at：日志记录时间 自动填充

方法中设置：

	// 1.2：title 【请求处理方法中设置】
	// 1.3：business_type 【请求处理方法中设置】
	// 1.4：origin_value 【编辑方法中设置】
	// 1.5：value 【编辑方法中设置】
	// 1.19：description：描述信息 【请求处理方法中设置】

中间件中设置：

	// 1.6：request_method 获取请求方式
	// 1.7：url 获取请求URL
	// 1.8：method 获取请求方法名称【具体方法中获取 使用反射】
	// 1.9：param 获取请求参数
	// 1.10：result 获取返回数据
	// 1.11：status 获取请求处理状态
	// 1.12：error_msg：获取异常信息
	// 1.13：oper_id：获取操作人id
	// 1.14：oper_name：获取操作人名称
	// 1.15：dept_name：获取操作人部门名称
	// 1.16：ip：获取操作人客户端ip
	// 1.17：location：获取操作人操作地点
	// 1.18：oper_type：获取操作类型

gorm自动填充：

	     // 1.1：id 自增
		// 1.20：created_at：日志记录时间 自动填充
*/
func LogHandler(ctx *gin.Context) {

	writer := util.ResponseWriter{
		ctx.Writer,
		bytes.NewBuffer([]byte{}),
	}
	ctx.Writer = writer

	//1. 获取日志记录数据
	// 1.6：request_method 获取请求方式
	requestMethod := ctx.Request.Method
	// 1.7：url 获取请求URL
	url := ctx.Request.RequestURI
	// 1.9：param 获取请求参数
	paramMap, paramStr := getParameter(ctx)
	// 将参数存储到上下文中
	ctx.Set("paramMap", paramMap)

	// 1.13：oper_id：获取操作人id 【实际情况 可从token中获取 | 从前面的认证中间件中获取】
	// 1.14：oper_name：获取操作人名称
	// 1.15：dept_name：获取操作人部门名称
	operId := int64(1)
	operName := "admin"
	deptName := ""

	// 1.16：ip：获取操作人客户端ip
	ip := util.GetClientIp(ctx.Request)
	// 1.17：location：获取操作人操作地点
	location := util.GetLocationByIp(ip)
	// 1.18：oper_type：获取操作类型 【可从token中获取 | 从前面的认证中间件中获取】
	operType := model.OperType_OTHER

	//2.调用方法执行操作
	ctx.Next()

	// 1.8：method 获取请求方法名称【具体方法中获取 使用反射】
	//sysLog.Method = util.GetCurrentMethodName()
	methodName := runtime.FuncForPC(reflect.ValueOf(ctx.Handler()).Pointer()).Name()
	// 1.10：result 获取返回数据
	ctx.Writer = writer
	result := writer.B.String()
	// 1.11：status 获取请求处理状态
	// 1.12：error_msg：获取异常信息
	var status int
	var errorMsg string
	var response model.Response
	json.Unmarshal([]byte(result), &response)
	if response.Success == false {
		status = model.OperStatus_ABNORMAL
		errorMsg = response.Message
	}
	//获取模块标题和业务类型
	title := ctx.GetString("logTitle")
	businessType := ctx.GetInt("logBusType")
	originValue := ctx.GetString("logOriginValue")
	value := ctx.GetString("logValue")
	description := ctx.GetString("logDescription")

	sysLog := &model.SysOperLog{
		Title:         title,
		BusinessType:  businessType,
		OriginValue:   originValue,
		Value:         value,
		RequestMethod: requestMethod,
		Url:           url,
		Method:        methodName,
		Param:         paramStr,
		Result:        result,
		Status:        status,
		ErrorMsg:      errorMsg,
		OperId:        operId,
		OperName:      operName,
		DeptName:      deptName,
		Ip:            ip,
		Location:      location,
		OperType:      operType,
		Description:   description,
	}

	//3.异步操作 将日志写入到数据库
	go service.WriteLog(sysLog)
}

func getParameter(ctx *gin.Context) (map[string]interface{}, string) {
	// 获取路径参数
	params := ctx.Params
	// 获取get请求url拼接参数
	queryParams := ctx.Request.URL.Query()
	// 获取请求体中的参数
	paramMap := make(map[string]interface{})
	//获取请求内容类型
	contentType := ctx.GetHeader("Content-Type")
	if strings.Contains(contentType, "application/x-www-form-urlencoded") {
		// 表单数据
		ctx.ShouldBind(&paramMap)
	} else if strings.Contains(contentType, "application/json") {
		// JSON 数据
		ctx.ShouldBindJSON(&paramMap)
	}

	// 合并参数
	//路径参数 	/demo/1
	for _, p := range params {
		paramMap[p.Key] = p.Value
	}
	//get请求url拼接参数  /demo?aa=1
	for key, values := range queryParams {
		paramMap[key] = values
	}
	if paramMap != nil && len(paramMap) != 0 {
		data, _ := json.Marshal(paramMap)
		return paramMap, string(data)
	} else {
		return nil, ""
	}
}
